#include "Function.hpp"
#include "EquationSolver.hpp"
#include <iostream>
#include <cmath>

const double Pi = acos(-1.);

class F1 : public Function {
public:
    double operator() (double x) const{
        return 1.0/x-tan(x);
    }
};
class F2 : public Function {
public:
    double operator() (double x) const {
        return 1.0 / x - std::pow(2.0, x); 
    }
};
class F3 : public Function {
public:
    double operator() (double x) const {
        return std::pow(2.0, -x) + std::exp(x) + 2 * std::cos(x) - 6;
    }
};
class F4 : public Function {
public:
    double operator() (double x) const {
        return (std::pow(x, 3) + 4 * std::pow(x, 2) + 3 * x + 5) / 
               (2 * std::pow(x, 3) - 9 * std::pow(x, 2) + 18 * x - 2);
    }
};
class F5 : public Function {
public:
    double operator() (double x) const {
        return x - tan(x);
    }
};
class F6 : public Function {
public:
    double operator() (double x) const {
        return sin(x/2)-1;
    }
};
class F7 : public Function {
public:
    double operator() (double x) const {
        return exp(x) - tan(x);
    }
};
class F8 : public Function {
public:
    double operator() (double x) const {
        return pow(x,3)-12*pow(x,2)+3*x+1;
    }
};
class F9 : public Function {
public:
    double operator() (double x) const {
        return 12.4-10*(0.5*Pi-asin(x)-x*sqrt(1-x*x));
    }
};
class F10 : public Function {
public:
    double operator() (double x) const {
        double beta1=11.5*Pi/180.0;
        double l=89.0,h=49.0,D=55.0;
        double A = l * sin(beta1);
        double B = l * cos(beta1);
        double C = (h + 0.5 * D) * sin(beta1) - 0.5 * D * tan(beta1);
        double E = (h + 0.5 * D) * cos(beta1) - 0.5 * D;
        return A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x);    
    }
};
class F11 : public Function {
public:
    double operator() (double x) const {
        double beta1=11.5*Pi/180.0;
        double l=89.0,h=49.0,D=30.0;
        double A = l * sin(beta1);
        double B = l * cos(beta1);
        double C = (h + 0.5 * D) * sin(beta1) - 0.5 * D * tan(beta1);
        double E = (h + 0.5 * D) * cos(beta1) - 0.5 * D;
        return A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x);    
    }
};
class F12 : public Function {
public:
    double operator() (double x) const {
        double beta1=11.5*Pi/180.0;
        double l=89.0,h=49.0,D=55.0;
        double A = l * sin(beta1);
        double B = l * cos(beta1);
        double C = (h + 0.5 * D) * sin(beta1) - 0.5 * D * tan(beta1);
        double E = (h + 0.5 * D) * cos(beta1) - 0.5 * D;
        return A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x);    
    }
};
class F13 : public Function {
public:
    double operator() (double x) const {
        double beta1=11.5*Pi/180.0;
        double l=89.0,h=49.0,D=55.0;
        double A = l * sin(beta1);
        double B = l * cos(beta1);
        double C = (h + 0.5 * D) * sin(beta1) - 0.5 * D * tan(beta1);
        double E = (h + 0.5 * D) * cos(beta1) - 0.5 * D;
        return A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x);    
    }
};
void solve_f1() {
    std::cout << "B(1):Solving x^{-1} - \\tan x on [0, \\pi/2]" << std::endl;
    Bisection_Method solver_f1(F1(), 0.00001, Pi/2-0.00001);
    double x = solver_f1.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f2() {
    std::cout << "B(2):Solving x^{-1} - \\2^(x) on [0, 1]" << std::endl;
    Bisection_Method solver_f2(F2(),0.00001,1.0);
    double x = solver_f2.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f3() {
    std::cout << "B(3):Solving 2^(-x) + \\e^(x) + \\2cos(x) - \\6 on [1, 3]" << std::endl;
    Bisection_Method solver_f3(F3(),1,3);
    double x = solver_f3.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f4() {
    std::cout << "B(4):Solving (x^3 + 4x^2 + 3x + 5) \\ (2x^3-9x^2+18x-2) on [0, 4]" << std::endl;
    Bisection_Method solver_f4(F4(),0,4);
    double x = solver_f4.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f5() {
    std::cout << "C:Solving x = tan(x) on [4.5, 7.7]" << std::endl;
    Newton_Method solver_f5(F5(),4.5);
    double x = solver_f5.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f6() {
    std::cout << "D(1):Solving sin(x/2) - 1 with x0=1 , x1=Pi/2" << std::endl;
    Secant_Method solver_f6(F6(),1.0,Pi/2.0);
    double x = solver_f6.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f61() {
    std::cout << "D(1):Solving sin(x/2) - 1 with x0=1 , x1=Pi/4" << std::endl;
    Secant_Method solver_f6(F6(),1.0,Pi/4);
    double x = solver_f6.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f7() {
    std::cout << "D(2):Solving e^x - tan(x) with x0=1 , x1=1.4" << std::endl;
    Secant_Method solver_f7(F7(),1,1.4);
    double x = solver_f7.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f71() {
    std::cout << "D(2):Solving e^x - tan(x) with x0=0.5 , x1=2.0" << std::endl;
    Secant_Method solver_f7(F7(),0.5,2.0);
    double x = solver_f7.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f8() {
    std::cout << "D(3):Solving x^3-12x^2+3x+1 with x0=0 , x1=-0.5" << std::endl;
    Secant_Method solver_f8(F8(),0,-0.5);
    double x = solver_f8.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f81() {
    std::cout << "D(3):Solving x^3-12x^2+3x+1 with x0=0.1 , x1=-1.0" << std::endl;
    Secant_Method solver_f8(F8(),0.1,-1.0);
    double x = solver_f8.solve();
    std::cout << "A root is: " << x << std::endl;
}
void solve_f9() {
    std::cout << "E:Find the depth of water in the trough to within 0.01ft by each of the three implementations in A" << std::endl;
    Secant_Method solver_f9(F9(), 0.000000001, 0.01);
    double x = solver_f9.solve();
    printf("The depth of water is: %.2f\n", x);
}
void solve_f10() {
    std::cout << "F(1):Solving A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x)" << std::endl;
    Newton_Method solver_f10(F10(),33*Pi/180);
    double x = solver_f10.solve();
    std::cout << "A root is: " << x*180/Pi << std::endl;
}
void solve_f11() {
    std::cout << "F(2)Solving A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x) with D=30" << std::endl;
    Newton_Method solver_f11(F11(),33*Pi/180);
    double x = solver_f11.solve();
    std::cout << "A root is: " << x*180/Pi << std::endl;
}
void solve_f12() {
    std::cout << "F(3):Solving A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x) with another initial value=32.9" << std::endl;
    Secant_Method solver_f12(F12(),32.9*Pi/180,33*Pi/180);
    double x = solver_f12.solve();
    std::cout << "A root is: " << x*180/Pi << std::endl;
    
}
void solve_f13() {
    std::cout << "Solving A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x) with another initial value=30" << std::endl;
    Secant_Method solver_f13(F13(),30*Pi/180,33*Pi/180);
    double x = solver_f13.solve();
    std::cout << "A root is: " << x*180/Pi << std::endl;
}
void solve_f14() {
    std::cout << "Solving A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x) with another initial value=-1000" << std::endl;
    Secant_Method solver_f13(F13(),-1000*Pi/180,33*Pi/180);
    double x = solver_f13.solve();
    std::cout << "A root is: " << x*180/Pi << std::endl;
}
void solve_f15() {
    std::cout << "Solving A*sin(x)*cos(x)+B*pow(sin(x), 2)-C*cos(x)-E*sin(x) with another initial value=-1e100" << std::endl;
    Secant_Method solver_f13(F13(),-1e100*Pi/180,33*Pi/180);
    double x = solver_f13.solve();
    std::cout << "A root is: " << x*180/Pi << std::endl;
}
/* Type your code here */

int main() {
    solve_f1();
    solve_f2();
    solve_f3();
    solve_f4();
    solve_f5();
    solve_f6();
    solve_f61();
    solve_f7();
    solve_f71();
    solve_f8();
    solve_f81();
    solve_f9();
    solve_f10();
    solve_f11();
    solve_f12();
    solve_f13();
    solve_f14();
    solve_f15();
    /* Type your code here */
    return 0;
}
